/**HEADER********************************************************************
* 
* Copyright (c) 2008 Freescale Semiconductor;
* All Rights Reserved
*
* Copyright (c) 2004-2010 Embedded Access Inc.;
* All Rights Reserved
*
* Copyright (c) 1989-2008 ARC International;
* All Rights Reserved
*
*************************************************************************** 
*
* THIS SOFTWARE IS PROVIDED BY FREESCALE "AS IS" AND ANY EXPRESSED OR 
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  
* IN NO EVENT SHALL FREESCALE OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
* THE POSSIBILITY OF SUCH DAMAGE.
*
**************************************************************************
*
* $FileName: mpc83xx_etsec_receive.c$
* $Version : 3.8.1.0$
* $Date    : May-12-2011$
*
* Comments:
*
*            This file contains the QUICC Ethernet multicasting
*            interface functions.
*
*END************************************************************************/

#include <mqx.h>
#include <bsp.h>
#include <enet.h>
#include <enetprv.h>
#include <mpc83xx_etsec_prv.h>


/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : MPC83xx_etsec_rx_add
*  Returned Value : void
*  Comments       :
*        Enqueues a PCB onto the receive ring.
*
*END*-----------------------------------------------------------------*/

void MPC83xx_etsec_rx_add(PCB_PTR  pcb_ptr)
{ 
   ENET_CONTEXT_STRUCT_PTR      enet_ptr;
   VMPC83xx_TSEC_BD_STRUCT_PTR  bd_ptr;
   MPC83xx_ETSEC_STRUCT_PTR      tsec_ptr;
   uint_32                      ctl;
   MPC83XX_TSEC_CONTEXT_STRUCT_PTR tsec_context_ptr;
   
   enet_ptr = (ENET_CONTEXT_STRUCT_PTR)pcb_ptr->PRIVATE;
   tsec_context_ptr = (MPC83XX_TSEC_CONTEXT_STRUCT_PTR)enet_ptr->MAC_CONTEXT_PTR;
   tsec_ptr = tsec_context_ptr->TSEC_ADDRESS;

   /*
   ** This function can be called from any context, and it needs mutual
   ** exclusion with itself.
   */
   etsec_lock();
   
   bd_ptr = &tsec_context_ptr->TSEC_RX_RING_PTR[tsec_context_ptr->LastRxBD];
   
   _DCACHE_INVALIDATE_MLINES((void *)bd_ptr, sizeof(VMPC83xx_TSEC_BD_STRUCT));

   /*
   ** The TSEC doesn't clear control bits when errors don't occur;
   ** it just sets bits when errors do occur.  So we clear all the
   ** control bits before enqueueing the descriptor.
   */
   ctl = bd_ptr->CONTROL;
   ctl &= (MPC83xx_TSEC_BD_RX_WRAP | MPC83xx_TSEC_BD_RX_INTERRUPT);
   ctl |=  MPC83xx_TSEC_BD_RX_EMPTY;

   /* Initialize the descriptor and give it to the eTSEC */
   bd_ptr->BUFFER = pcb_ptr->FRAG[0].FRAGMENT;

   bd_ptr->LENGTH = 0;
   _PSP_EIEIO();

   bd_ptr->CONTROL = ctl;
   _PSP_EIEIO();

   _DCACHE_FLUSH_MLINES((void *)bd_ptr, sizeof(VMPC83xx_TSEC_BD_STRUCT));
   /*
   ** Add the PCB to the receive PCB queue (linked via PRIVATE) and
   ** increment the tail to the next descriptor
   */
   QADD(tsec_context_ptr->RxPCBHead, tsec_context_ptr->RxPCBTail, pcb_ptr);
   RX_INC(tsec_context_ptr->LastRxBD);
   tsec_context_ptr->ActiveRxBDs++;
   
   if(tsec_ptr->RSTAT & MPC83xx_TSEC_RSTAT_QHLT) {
      tsec_ptr->RSTAT = MPC83xx_TSEC_RSTAT_QHLT;
   }
   etsec_unlock();

} 


/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : MPC83xx_etsec_rx_catchup
*  Returned Value : void
*  Comments       :
*        Processes received packets.
*
*END*-----------------------------------------------------------------*/

void MPC83xx_etsec_rx_catchup
   (
      /* [IN] the Ethernet state structure */
      ENET_CONTEXT_STRUCT_PTR  enet_ptr
   )
{ 
   MPC83XX_TSEC_CONTEXT_STRUCT_PTR tsec_context_ptr;
   VMPC83xx_TSEC_BD_STRUCT_PTR     bd_ptr;
   ENET_ECB_STRUCT_PTR             ecb_ptr;
   PCB_PTR                         pcb_ptr;
   uint_32                         status, length;
   boolean                         error;
   
   tsec_context_ptr = enet_ptr->MAC_CONTEXT_PTR;

   /* Dequeue all received packets */
   while (tsec_context_ptr->ActiveRxBDs > 0) {
      bd_ptr = &tsec_context_ptr->TSEC_RX_RING_PTR[tsec_context_ptr->NextRxBD];
      _DCACHE_INVALIDATE_MLINES((void *)bd_ptr, sizeof(VMPC83xx_TSEC_BD_STRUCT));
      if (bd_ptr->CONTROL & MPC83xx_TSEC_BD_RX_EMPTY) {
         break;
      } 

      QGET(tsec_context_ptr->RxPCBHead, tsec_context_ptr->RxPCBTail, pcb_ptr);
      pcb_ptr->PRIVATE = enet_ptr;
      RX_INC(tsec_context_ptr->NextRxBD);
      tsec_context_ptr->ActiveRxBDs--;

      ENET_INC_STATS(COMMON.ST_RX_TOTAL);
      status = bd_ptr->CONTROL;
      length = bd_ptr->LENGTH;

      error = FALSE;

      _dcache_invalidate_mlines((pointer)bd_ptr->BUFFER, length);

      if (status & MPC83xx_TSEC_BD_RX_ERROR)
      {
         error = TRUE;
      } 

      if (error ) {
         /* If an error occurred, record it and discard */
         ENET_INC_STATS(COMMON.ST_RX_ERRORS);
            
         if (status & MPC83xx_TSEC_BD_RX_OVERRUN_ERROR) {
            ENET_INC_STATS(ST_RX_OVERRUN);
         } 
         if (status & MPC83xx_TSEC_BD_RX_CRC_ERROR) {
            ENET_INC_STATS(ST_RX_FCS);
         } 
         if (status & MPC83xx_TSEC_BD_RX_SHORT) {
            ENET_INC_STATS(ST_RX_RUNT);
         } 
         if (status & MPC83xx_TSEC_BD_RX_NON_ALIGNED) {
            ENET_INC_STATS(ST_RX_ALIGN);
         } 
         if (status & MPC83xx_TSEC_BD_RX_TOO_LONG) {
            ENET_INC_STATS(ST_RX_GIANT);
         } 

      } else if (~status & MPC83xx_TSEC_BD_RX_FIRST_IN_FRAME) {

         /* If the packet didn't fit in one buffer, record and discard */
         ENET_INC_STATS(COMMON.ST_RX_ERRORS);
         ENET_INC_STATS(ST_RX_GIANT);

      } else if (length > (ENET_FRAMESIZE_MAXDATA)) {

         /* ERRATA ?: too long bit always set, need to check data length */
         ENET_INC_STATS(ST_RX_GIANT);
         ENET_INC_STATS(COMMON.ST_RX_ERRORS);

      } else {

 //        pcb_ptr->FRAG[0].LENGTH = length - ENET_FRAMESIZE_TAIL;
         ecb_ptr = (ENET_ECB_STRUCT_PTR)ENET_find_receiver(enet_ptr, (ENET_HEADER_PTR)bd_ptr->BUFFER, &length);
         pcb_ptr->FRAG[0].FRAGMENT = (uchar_ptr)bd_ptr->BUFFER;
         pcb_ptr->FRAG[0].LENGTH = length;
 
         if (ecb_ptr) {
            ecb_ptr->SERVICE(pcb_ptr, ecb_ptr->PRIVATE);
            pcb_ptr = NULL;
         } else {
            ENET_INC_STATS(COMMON.ST_RX_DISCARDED);
         } 
      } 

      if (pcb_ptr) {
         MPC83xx_etsec_rx_add(pcb_ptr);
      } 

   } 
} 


/*ISR*------------------------------------------------------------------
*
*  Function Name  : MPC83xx_etsec_RX_ISR
*  Returned Value : void
*  Comments       :
*        Receive Unit ISR
*
*END*-----------------------------------------------------------------*/

void MPC83xx_etsec_RX_ISR
   (
      /* [IN] the Ethernet state structure */
      ENET_CONTEXT_STRUCT_PTR  enet_ptr
   )
{
   MPC83XX_TSEC_CONTEXT_STRUCT_PTR  tsec_context_ptr;
   MPC83xx_ETSEC_STRUCT_PTR tsec_ptr;
   uint_32                 events;

   tsec_context_ptr = enet_ptr->MAC_CONTEXT_PTR;
   tsec_ptr = tsec_context_ptr->TSEC_ADDRESS;
   
   while (TRUE) {
      events = tsec_ptr->IEVENT;
      if (events & (MPC83xx_TSEC_IEVENT_RXB | MPC83xx_TSEC_IEVENT_RXF)) {
         tsec_ptr->IEVENT =(MPC83xx_TSEC_IEVENT_RXB | MPC83xx_TSEC_IEVENT_RXF);
         _PSP_SYNC();
         MPC83xx_etsec_rx_catchup(enet_ptr);
      } else {
      	break;
      }
   } 
}


/* EOF */
